package com.xunk.common.core.utils;


import com.alibaba.fastjson.JSONObject;
import com.xunk.common.core.config.BeanConfig;
import com.xunk.common.core.text.Convert;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.http.*;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 发送http请求可统一使用该类，TODO 改造方法，全部使用resttemplate发送，可打印日志
 * {@link BeanConfig#restTemplate(),HttpUtil#getRestTemplate(),com.xunk.common.core.interceptor.LoggingClientHttpRequestInterceptor#intercept(HttpRequest, byte[], ClientHttpRequestExecution)}
 */
public class HttpUtil {

    /**
     * 非负载均衡
     *
     * @return
     */
    public static RestTemplate getRestTemplate() {
        return SpringUtils.getBean("NLBRestTemplate");
    }

    private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("([^&=]+)(=?)([^&]+)?");

    public static Map<String, String> getHeader4Ruying(String appId, String jwtToken) {
        Assert.notNull(appId, "appid不能为空");
        Assert.notNull(jwtToken, "jwtToken不能为空");
        Map<String, String> header = new HashMap<>();
        header.put("App-Id", appId);
        header.put("Authorization", "Bearer " + jwtToken);
        return header;
    }

    public static JSONObject doGetstr(String url, Map<String, String> header) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        JSONObject jsonObject = null;
        try {
            if (header != null && header.size() > 0)
                for (Map.Entry<String, String> entry : header.entrySet())
                    httpGet.addHeader(entry.getKey(), entry.getValue());
            CloseableHttpResponse response = httpclient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity);
                jsonObject = JSONObject.parseObject(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static JSONObject doGetstr(String url) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        JSONObject jsonObject = null;
        try {
            CloseableHttpResponse response = httpclient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity);
                jsonObject = JSONObject.parseObject(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static JSONObject doPoststr(String url, String outStr, Map<String, String> header) {
        return post(url, outStr, header, JSONObject.class);
    }

    public static JSONObject doPoststr(String url, String outStr) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        JSONObject jsonObject = null;
        try {
            httpPost.setEntity(new StringEntity(outStr, "utf-8"));
            CloseableHttpResponse response = httpclient.execute(httpPost);
            String result = EntityUtils.toString(response.getEntity(), "utf-8");
            jsonObject = JSONObject.parseObject(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static JSONObject doDeletetr(String url, Map<String, String> header) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpDelete httpDelete = new HttpDelete(url);
        JSONObject jsonObject = null;
        try {
            if (header != null && header.size() > 0)
                for (Map.Entry<String, String> entry : header.entrySet())
                    httpDelete.addHeader(entry.getKey(), entry.getValue());
            CloseableHttpResponse response = httpclient.execute(httpDelete);
            String result = EntityUtils.toString(response.getEntity(), "utf-8");
            jsonObject = JSONObject.parseObject(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static JSONObject doPutstr(String url, String outStr, Map<String, String> header) {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        HttpPut httpPut = new HttpPut(url);
        JSONObject jsonObject = null;
        try {
            if (header != null && header.size() > 0)
                for (Map.Entry<String, String> entry : header.entrySet())
                    httpPut.addHeader(entry.getKey(), entry.getValue());
            httpPut.setEntity(new StringEntity(outStr, "utf-8"));
            CloseableHttpResponse response = httpclient.execute(httpPut);
            String result = EntityUtils.toString(response.getEntity(), "utf-8");
            jsonObject = JSONObject.parseObject(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static JSONObject doPostForm(String url, Map<String, String> param) {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        JSONObject jsonObject = null;
        try {
            if (param != null) {
                MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                param.forEach((key, value) -> {
                    builder.addPart(key, new StringBody(value, ContentType.create("text/plaiin", Consts.UTF_8)));
                });
                HttpEntity entity = builder.build();
                httpPost.setEntity(entity);
                CloseableHttpResponse response = httpClient.execute(httpPost);
                String result = EntityUtils.toString(response.getEntity(), Consts.UTF_8);
                jsonObject = JSONObject.parseObject(result);
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return jsonObject;
    }

    /**
     * get请求
     *
     * @param url
     * @param urlParam
     * @param headers
     * @return
     */
    public static <T> T get(String url, Map<String, Object> urlParam, Map<String, Object> headers, Class<T> clz) {
//        getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler());
        HttpHeaders requestHeaders = new HttpHeaders(createMultiValueMap(headers));
        org.springframework.http.HttpEntity<?> requestEntity = new org.springframework.http.HttpEntity<>(null, requestHeaders);
        final ResponseEntity<T> responseEntity = getRestTemplate().exchange(expandURL(url, urlParam.keySet()), HttpMethod.GET, requestEntity, clz);
        return responseEntity.getBody();
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @param mediaType
     * @param headers
     * @return
     */
    public static String post(String url, Map<String, Object> params, MediaType mediaType, Map<String, Object> headers) {
        // 拿到header信息
        HttpHeaders requestHeaders = new HttpHeaders(createMultiValueMap(headers));
        requestHeaders.setContentType(mediaType);
        org.springframework.http.HttpEntity<Map> requestEntity = (mediaType == MediaType.APPLICATION_JSON
                || mediaType == MediaType.APPLICATION_JSON_UTF8) ? new org.springframework.http.HttpEntity<>(params, requestHeaders)
                : new org.springframework.http.HttpEntity<>(null, requestHeaders);
//        getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler());
        String result = (mediaType == MediaType.APPLICATION_JSON || mediaType == MediaType.APPLICATION_JSON_UTF8)
                ? getRestTemplate().postForObject(url, requestEntity, String.class)
                : getRestTemplate().postForObject(expandURL(url, params.keySet()), requestEntity, String.class, params);
        return result;
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @param headers
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> T post(String url, Object params, Map headers, Class<T> clz) {
        // 拿到header信息
        HttpHeaders requestHeaders = new HttpHeaders(createMultiValueMap(headers));
        org.springframework.http.HttpEntity<?> requestEntity = new org.springframework.http.HttpEntity<>(params, requestHeaders);
//        getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler());
        return getRestTemplate().postForObject(url, requestEntity, clz);
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @param mediaType
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> T post(String url, Map<String, Object> params, MediaType mediaType, Map<String, Object> headers, Class<T> clz) {
        // 设置header信息
        HttpHeaders requestHeaders = new HttpHeaders(createMultiValueMap(headers));
        requestHeaders.setContentType(mediaType);

        org.springframework.http.HttpEntity<?> requestEntity = (
                mediaType == MediaType.APPLICATION_JSON
                        || mediaType == MediaType.APPLICATION_JSON_UTF8)
                ? new org.springframework.http.HttpEntity<>(params, requestHeaders)
                : (mediaType == MediaType.APPLICATION_FORM_URLENCODED
                ? new org.springframework.http.HttpEntity<MultiValueMap>(createMultiValueMap(params), requestHeaders)
                : new org.springframework.http.HttpEntity<>(null, requestHeaders));

//        getRestTemplate().setErrorHandler(new DefaultResponseErrorHandler());
        T result = (mediaType == MediaType.APPLICATION_JSON || mediaType == MediaType.APPLICATION_JSON_UTF8)
                ? getRestTemplate().postForObject(url, requestEntity, clz)
                : getRestTemplate().postForObject(mediaType == MediaType.APPLICATION_FORM_URLENCODED
                ? url
                : expandURL(url, params.keySet()), requestEntity, clz, params);

        return result;
    }

    /**
     * 创建headers
     *
     * @param params
     * @return
     */
    private static MultiValueMap<String, String> createMultiValueMap(Map<String, Object> params) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        for (String key : params.keySet()) {
            if (params.get(key) instanceof List) {
                for (Iterator<String> it = ((List<String>) params.get(key)).iterator(); it.hasNext(); ) {
                    String value = it.next();
                    map.add(key, value);
                }
            } else {
                map.add(key, Convert.toStr(params.get(key)));
            }
        }
        return map;
    }


    /**
     * 将参数拼接到url上
     *
     * @param url
     * @param keys
     * @return
     */
    private static String expandURL(String url, Set<?> keys) {
        Matcher mc = QUERY_PARAM_PATTERN.matcher(url);
        StringBuilder sb = new StringBuilder(url);
        if (mc.find()) {
            sb.append("&");
        } else {
            sb.append("?");
        }

        for (Object key : keys) {
            sb.append(key).append("=").append("{").append(key).append("}").append("&");
        }
        return sb.deleteCharAt(sb.length() - 1).toString();
    }

    /**
     * 异常处理器
     */
    private static class DefaultResponseErrorHandler implements ResponseErrorHandler {

        @Override
        public boolean hasError(ClientHttpResponse response) throws IOException {
            return response.getStatusCode().value() != HttpServletResponse.SC_OK;
        }

        @Override
        public void handleError(ClientHttpResponse response) throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()));
            StringBuilder sb = new StringBuilder();
            String str = null;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            try {
                throw new Exception(sb.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


}
